package org.example;

import static argo.jdom.JsonNodeBuilders.*;
import argo.format.PrettyJsonFormatter;
import argo.jdom.JsonArrayNodeBuilder;
import argo.jdom.JsonObjectNodeBuilder;
import org.jsoup.nodes.Document;

import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.StandardOpenOption;

import java.time.format.DateTimeFormatter;
import java.util.HashSet;
import java.util.Set;
import java.util.TreeSet;

public class DataCollector extends HTMLMetroParser {
    DataCollector(Document document, String dataRoot) throws IOException, DataFormatException {
        super(document);

        CSVDateParser dateParser = CSVDateParser.parser(dataRoot);
        JSONDepthParser depthParser = JSONDepthParser.parser(dataRoot);

        for (MetroStation station : getMetroStations()) {
            station.setDate(dateParser.getStationDate(station.getName()));
            station.setDepth(depthParser.getStationDepth(station.getName()));
        }
    }

    public static DataCollector collector(Document document, String dataRoot) throws IOException, DataFormatException {
        return new DataCollector(document, dataRoot);
    }

    public void saveFile(String file, String text) throws IOException {
        Path path = Path.of(file);
        Files.createDirectories(path.getParent());
        Files.writeString(path, text, StandardOpenOption.TRUNCATE_EXISTING, StandardOpenOption.CREATE);
    }

    public String toStationsJsonString() {
        DateTimeFormatter dateFormatter = DateTimeFormatter.ofPattern("dd.MM.yyyy");
        JsonArrayNodeBuilder stations = anArrayBuilder();

        for (MetroStation metroStation : new TreeSet<>(getMetroStations())) {
            JsonObjectNodeBuilder station = anObjectBuilder()
                    .withField("name", aStringBuilder(metroStation.getName()))
                    .withField("line", aStringBuilder(getMetroLine(metroStation.getLineNumber()).getName()));

            if (metroStation.getDate() != null) {
                station = station.withField("date", aStringBuilder(metroStation.getDate().format(dateFormatter)));
            }

            if (metroStation.getDepth() != null) {
                station = station.withField("depth", aNumberBuilder(metroStation.getDepth().toString()));
            }

            station = station.withField("hasConnection", metroStation.hasConnection() ? aTrueBuilder() : aFalseBuilder());

            stations = stations.withElement(station);
        }

        return new PrettyJsonFormatter().format(
                anObjectBuilder().withField("stations", stations).build()
        );
    }

    public String toMapJsonString() {
        JsonArrayNodeBuilder lines = anArrayBuilder();
        JsonObjectNodeBuilder stations = anObjectBuilder();

        for (MetroLine metroLine : getMetroLines()) {

            lines = lines.withElement(anObjectBuilder()
                    .withField("number", aStringBuilder(metroLine.getNumber()))
                    .withField("name", aStringBuilder(metroLine.getName()))
            );

            JsonArrayNodeBuilder station = anArrayBuilder();

            for (MetroStation metroStation : getMetroStations()) {
                if (metroStation.getLineNumber().equals(metroLine.getNumber())) {
                    station = station.withElement(aStringBuilder(metroStation.getName()));
                }
            }

            stations = stations.withField(metroLine.getNumber(), station);
        }

        JsonArrayNodeBuilder connections = anArrayBuilder();
        Set<MetroStation> stationsDone = new HashSet<>();

        for (MetroStation metroStation : getMetroStations()) {
            if (metroStation.hasConnection() && !stationsDone.contains(metroStation)) {

                JsonArrayNodeBuilder connection = anArrayBuilder()
                        .withElement(anObjectBuilder()
                            .withField("line", aStringBuilder(metroStation.getLineNumber()))
                            .withField("station", aStringBuilder(metroStation.getName())
                        )
                );

                for (MetroStation connectedStation : metroStation.getConnections()) {
                    stationsDone.add(connectedStation);

                    connection = connection
                            .withElement(anObjectBuilder()
                                    .withField("line", aStringBuilder(connectedStation.getLineNumber()))
                                    .withField("station", aStringBuilder(connectedStation.getName())
                                    )
                            );
                }

                connections = connections.withElement(connection);
            }
        }

        JsonObjectNodeBuilder map = anObjectBuilder()
                .withField("stations", stations)
                .withField("connections", connections)
                .withField("lines", lines);

        return new PrettyJsonFormatter().format(map.build());
    }
}
